#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <UIKit/UIKit.h>
#import <UIKit/UIApplication.h>
#import <Preferences/PSSpecifier.h>
#import <EventKit/EventKit.h>
#import <LockInfo/Plugin.h>

#import "framework/FWCommon.h"
#import "framework/FWApplication.h"

#import "PICommon.h"
#import "PIIntegration.h"
#import "PIContextObject.h"
#import "PIPlugin.h"

#import "PICalendarPrefs.h"


static NSString* const kIsPocketInformant = @"isPocketInformant";
static NSString* const kCalendarKey = @"calendarKey";
static NSString* const kCalendarTitle = @"calendarTitle";
static NSString* const kCalendarEntries = @"calendarEntries";

static NSString* const kCalendarGroupOthers = @"[[[[[[[[[[";
static NSString* const kCalendarGroupInformant = @"]]]]]]]]]]]]";


@implementation PICalendarPrefs

@synthesize hiddenNative, hiddenPI;


// ******************************************************************************************
#pragma mark -
#pragma mark Initialisation and object management

- (id)initForContentSize:(CGSize)contentSize
{
	DebugLog(@"LI:PICommon: initForContentSize ##########################################");
    
    [_specifiers release];
    _specifiers = nil;

    [hiddenPI release];
    [hiddenNative release];
    
    nativeCalendars = NO;
    
    if ([super respondsToSelector:@selector(initForContentSize:)])
        self = [super initForContentSize:contentSize];
    else
        self = [super init];
    
    return self;
}

- (void)dealloc
{
	DebugLog(@"LI:PICommon: dealloc ##########################################");
    
    [_specifiers release];
    _specifiers = nil;

    [hiddenPI release];
    [hiddenNative release];
    
    [super dealloc];
}


// ******************************************************************************************
#pragma mark -
#pragma mark Get switch state

- (CFBooleanRef)stateForSpecifier:(PSSpecifier*)spec
{
    BOOL isPI = [[spec propertyForKey:kIsPocketInformant] boolValue];
    NSString* item = [spec propertyForKey:kCalendarKey];
    NSString* key = [self.specifier.properties valueForKey:@"defaults"];
    [spec.properties setValue:key forKey:@"defaults"];
    
    if (!self.hiddenPI) {
        NSArray* hidden = (NSArray*)CFPreferencesCopyAppValue((CFStringRef)kPIOptionsHiddenPICalendars, (CFStringRef)key);
        self.hiddenPI = hidden ? [NSMutableArray arrayWithArray:hidden] : [NSMutableArray arrayWithCapacity:10];
        [hidden release];
    }
    
    if (!self.hiddenNative && nativeCalendars) {
        NSArray* hidden = (NSArray*)CFPreferencesCopyAppValue((CFStringRef)kPIOptionsHiddenNativeCalendars, (CFStringRef)key);
        self.hiddenNative = hidden ? [NSMutableArray arrayWithArray:hidden] : [NSMutableArray arrayWithCapacity:10];
        [hidden release];
    }
    
    DebugLog(@"LI:PICommon: Getting value for %@, pi=%i item=%@, key=%@, native = %@, pi=%@",
             spec.name, isPI, item, key, self.hiddenNative, self.hiddenPI);
    
    if (isPI)
        return [hiddenPI indexOfObject:item] == NSNotFound ? kCFBooleanTrue : kCFBooleanFalse;
    else
        return [hiddenNative indexOfObject:item] == NSNotFound ? kCFBooleanTrue : kCFBooleanFalse;
}


// ******************************************************************************************
#pragma mark -
#pragma mark Get list of calendars

- (void)setState:(CFBooleanRef)show forSpecifier:(PSSpecifier*)spec
{
    DebugLog(@"LI:PICal: set %@ value %@", spec, show);
    
    NSString* key = [spec propertyForKey:@"defaults"];
    NSString* item = [spec propertyForKey:kCalendarKey];
    BOOL isPI = [[spec propertyForKey:kIsPocketInformant] boolValue];
    
    if (!self.hiddenPI)
        self.hiddenPI = [NSMutableArray arrayWithCapacity:10];
    if (!self.hiddenNative && nativeCalendars)
        self.hiddenNative = [NSMutableArray arrayWithCapacity:10];
    
    if (!CFBooleanGetValue(show)) {
        // We need to add our item to the correct hide list
        if (isPI) {
            // Add to hidden PI list if not there already 
            if ([hiddenPI indexOfObject:item] == NSNotFound)
                [hiddenPI addObject:item];
        }
        else {
            // Add to hidden native list if not there already 
            if ([hiddenNative indexOfObject:item] == NSNotFound)
                [hiddenNative addObject:item];
        }
    }
    else {
        // We need to remove our item from the correct hide list
        if (isPI) {
            NSInteger idx = [hiddenPI indexOfObject:item];
            if (idx != NSNotFound)
                [hiddenPI removeObjectAtIndex:idx];
        }
        else {
            NSInteger idx = [hiddenNative indexOfObject:item];
            if (idx != NSNotFound)
                [hiddenNative removeObjectAtIndex:idx];
        }
    }
    
    DebugLog(@"LI:PICommon: Key %@", key);
    DebugLog(@"LI:PICommon: Hidden native calendars %@", hiddenNative);
    DebugLog(@"LI:PICommon: Hidden pocket informant calendars %@", hiddenPI);
    
    if (isPI)
        CFPreferencesSetAppValue((CFStringRef)kPIOptionsHiddenPICalendars, hiddenPI, (CFStringRef)key);
    else
        CFPreferencesSetAppValue((CFStringRef)kPIOptionsHiddenNativeCalendars, hiddenNative, (CFStringRef)key);
    CFPreferencesAppSynchronize((CFStringRef)key);
}


// ******************************************************************************************
#pragma mark -
#pragma mark Get list of calendars

NSInteger compareCalendarGroups(id grp1, id grp2, void *context)
{
    return [[grp1 objectForKey:kCalendarTitle] compare:[grp2 objectForKey:kCalendarTitle]];
}

- (NSArray *)specifiers
{
    if (_specifiers == nil) {
        NSMutableArray* calendarGroups = [NSMutableArray arrayWithCapacity:5];
        NSMutableArray* calendarGroupNames = [NSMutableArray arrayWithCapacity:5];
        
        if (nativeCalendars) {
            EKEventStore* eventStore = [[EKEventStore alloc] init];
            for (EKCalendar* calendar in eventStore.calendars) {
                NSString* accountName = [calendar respondsToSelector:@selector(accountName)] ? [calendar accountName] : nil;
                if (!accountName)
                    accountName = kCalendarGroupOthers;
                
                NSInteger calendarGroupIdx = [calendarGroupNames indexOfObject:accountName];
                if (calendarGroupIdx != NSNotFound) {
                    NSMutableArray* entries = [[calendarGroups objectAtIndex:calendarGroupIdx] objectForKey:kCalendarEntries];
                    [entries addObject:[PIContextObject objectWithKey:0
                                                                 name:calendar.title
                                                                 icon:nil
                                                                color:nil
                                                               native:YES]];
                }
                else {
                    [calendarGroupNames addObject:accountName];
                    PIContextObject* entry = [PIContextObject objectWithKey:0
                                                                       name:calendar.title
                                                                       icon:nil
                                                                      color:nil
                                                                     native:YES];
                    NSMutableArray* entries = [NSMutableArray arrayWithCapacity:5];
                    [entries addObject:entry];
                    [calendarGroups addObject:[NSDictionary dictionaryWithObjectsAndKeys:
                                               entries, kCalendarEntries,
                                               accountName, kCalendarTitle,
                                               nil]];
                }
            }
            [eventStore release];
        }
        
        UIPasteboard* pasteBoard = [UIPasteboard pasteboardWithName:kPIStatusPasteboard create:NO];
        if (NSString* dbPath = FWStringValueFromData([pasteBoard valueForPasteboardType:kPIPasteboardDBPath])) {
            FWApplication* app = [[FWApplication alloc] init];
            app.databasePath = dbPath;
            NSArray* piCalendars = [PIIntegration arrayOfCalendars:app includeNative:NO];
            if (piCalendars.count > 0) {
                [calendarGroups addObject:[NSDictionary dictionaryWithObjectsAndKeys:
                                           piCalendars, kCalendarEntries,
                                           kCalendarGroupInformant, kCalendarTitle,
                                           nil]];
            }
            [app release];
        }
        
        // Sort all the names
        [calendarGroups sortUsingFunction:compareCalendarGroups context:nil];
        for (NSDictionary* group in calendarGroups) {
            [[group objectForKey:kCalendarEntries] sortUsingFunction:compareContextObjectNames context:nil];
        }
        
        NSMutableArray* specifiers = [NSMutableArray arrayWithCapacity:calendarGroupNames.count];
        NSBundle* bundle = [NSBundle bundleForClass:[self class]];
        for (NSDictionary* group in calendarGroups) {
            NSString* groupName = [group objectForKey:kCalendarTitle];
            NSNumber* isPI = [NSNumber numberWithBool:[groupName isEqualToString:kCalendarGroupInformant]];
            if (isPI.boolValue)
                groupName = localize(bundle, @"Filters/Informant Calendars");
            else if ([groupName isEqualToString:kCalendarGroupOthers])
                groupName = localize(bundle, @"Filters/Other Calendars");
            
            // Only add a group name if there are native calendars
            if (nativeCalendars)
                [specifiers addObject:[PSSpecifier groupSpecifierWithName:groupName]];
            
            for (PIContextObject* calendar in [group objectForKey:kCalendarEntries]) {
                PSSpecifier* ps = [PSSpecifier preferenceSpecifierNamed:calendar.name
                                                                 target:self
                                                                    set:@selector(setState:forSpecifier:)
                                                                    get:@selector(stateForSpecifier:)
                                                                 detail:Nil
                                                                   cell:PSSwitchCell
                                                                   edit:Nil];
                
                [ps setProperty:isPI forKey:kIsPocketInformant];
                [ps setProperty:calendar.isNative ? calendar.name : [[NSNumber numberWithInt:calendar.key] stringValue]
                         forKey:kCalendarKey];
                
                [specifiers addObject:ps];
            }
        }
        
        _specifiers = [specifiers retain];
        
        self.hiddenNative = nil;
        self.hiddenPI = nil;
    }
    
    return _specifiers;
}

@end
